package za.org.jcicada.comms.impl.tcp;

/**
 * CommsBidirectionalQueue.java
 *
 * @author Simon Cross
 *
 * Created on 7 January 2009, 15:36
 */

import java.io.IOException;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.comms.IMessageListener;
import za.org.jcicada.comms.impl.SelectableConnection;
import za.org.jcicada.comms.messages.CommsMessage;

/**
 * A TCP implementation of a katcomms ICommsBidirectionalQueue.
 * 
 * Provides the necessary functionality for setting up, using and managing
 * two-way network communication using a tcp socket.
 */
public class TcpConnection extends SelectableConnection {

	private static Log logger = LogFactory.getLog(TcpConnection.class);

	/** Socket connection to other side of bidirectional queue. */
	protected SocketChannel channel;

	public TcpConnection(SocketChannel channel) {
		this.channel = channel;
		commsBuffer = new TcpCommsBuffer();
	}

	@Override
	public void initialize(IMessageListener listener) throws IOException {
		super.initialize(listener);
		TcpSelector.getInstance().addReader(channel, this);
	}

	@Override
	public void close() throws IOException {
		logger.info(toString() + " closing");
		getSocket().close();
		try {
			getListener().onClose();
		} catch (Exception ex) {
			throw new IOException("Failed to close listener.", ex);
		}
	}
	
	@Override
	public boolean isClosed() {
		return getSocket().isClosed();
	}

	@Override
	public void sendMessage(CommsMessage message) throws IOException {
		ByteBuffer sendBuffer = commsBuffer.toByteBuffer(message);
		sendBuffer.flip();
		TcpSelector.getInstance().send(channel, sendBuffer);
	}
	
	@Override
	public int read(ByteBuffer dst) throws IOException {
		dst.clear();
		int numRead = channel.read(dst);
		dst.flip();
		feedBuffer(dst);
		return numRead;
	}
	
	@Override
	public int write(ByteBuffer src) throws IOException {
		return channel.write(src);
	}
	
	@Override
	public String toString() {
		return super.toString() + ":" + getSocket().getLocalSocketAddress();
	}
	
	public Socket getSocket() {
		return channel.socket();
	}
	
}
